
/// SOLUZIONE CON J VARIABILI TARGA STATICHE

#include "channel.h"

#include "shm_common.h"
#include <sys/shm.h>
#include <pthread.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>


struct channel_t
{
	union {
		struct {
			int type; // inheredited from base class (see shm.c)

			key_t key;
			int id;

			key_t altKey; // when set, receiver is receiving on this channel thru an Alternative

			pthread_mutex_t mutex;
			pthread_cond_t cond;

			int size; // message size
			int fixedsize; // message size as a multiple of 8 bytes (due to cache line problems...)
			int messages; // sent messages counter (0 or 1)
			int j; // how many static vtgs are allocated in memory
			int nextVtg; // which is the next vtg to use
			int currVtg;
		};
		char null[1024];
	};
};

// returns the address of the i-th message of channel ch
#define MSG(ch,i) ( (char*) ch + sizeof(*ch) + ch->fixedsize*i )



// creates a new channel
// the channel will contain 1+j vtgs
// j has to be at least 1, for the channel to be safe
struct channel_t *LCcreate_ss( key_t key, size_t size, int j )
{
	struct channel_t *ch;
	int fixedsize = size + ( LINE_SIZE - size%LINE_SIZE );

	int id = shmget( key, sizeof(*ch) + fixedsize*(1+j), IPC_CREAT | 0700 );
	if( id == -1 ) {
		printf( "receiver::shmget(%x[%d], %d, %x): %s\n", key, key, (int) ( sizeof(*ch) + fixedsize*(1+j) ), IPC_CREAT | 0700, strerror(errno) );
		return NULL;
	}

	// ch = shmat( id, 0, SHM_RDONLY );
	ch = shmat( id, 0, 0 );
	if( ch == (void*) -1 ) {
		printf( "receiver::shmat(%d, %d, %d): %s\n", id, 0, 0, strerror(errno) );
		return NULL;
	}

	// setting channel type
	ch->type = SYNCHRONOUS_SYMMETRIC;

	// setting channel key and id
	ch->key = key;
	ch->id = id;

	// initing mutex and condition variable
	// (it's mandatory to set them as shared objects!)
	mutex_init( & ch->mutex );
	cond_init( & ch->cond );

	// setting other fields of the channel
	ch->size = size;
	ch->fixedsize = fixedsize;
	ch->messages = 0;

	ch->j = 1+j;
	ch->nextVtg = 0;
	ch->currVtg = -1;

	ch->altKey = 0;

	return ch;
}

inline size_t LCgetChannelTypeSize_ss( struct channel_t *ch )
{
	return ch->size;
}

inline bool LCreadyToReceive_ss( struct channel_t *ch )
{
	return ch->messages > 0;
}
inline bool LCreadyToSend_ss( struct channel_t *ch )
{
	return ch->messages == 0;
}

// maps a shared channel on the virtual memory
struct channel_t *LCattach_ss( key_t key, size_t size, int j )
{
	struct channel_t *ch;
	int id = shmget( key, sizeof(*ch) + size*(1+j), 0700 );
	if( id == -1 ) {
		printf( "[SS] sender::shmget(): %s\n", strerror(errno) );
		return NULL;
	}

	ch = shmat( id, 0, 0 );
	if( ! ch ) {
		printf( "[SS] sender::shmat(): %s\n", strerror(errno) );
		return NULL;
	}

	return ch;
}
struct channel_t *LCreattach_ss( struct channel_t *ch )
{
	key_t key = ch->key;
	size_t size = ch->fixedsize;
	int j = ch->j-1;

	return LCattach_ss( key, size, j );
}

// destroys a channel
void LCdestroy_ss( struct channel_t *ch )
{
	pthread_mutex_destroy( & ch->mutex );
	pthread_cond_destroy( & ch->cond );

	shmctl( ch->id, IPC_RMID, 0 );
}

// removes a channel from the virtual memory
void LCdetatch_ss( struct channel_t *ch )
{
	shmdt( ch );
}

// sends a message on a channel
void LCsend_ss( struct channel_t *ch, char *msg )
{
	__LClockChannel( ch );

	memcpy( MSG(ch,ch->nextVtg), msg, ch->size );
	ch->messages ++;
	ch->nextVtg = ( ch->nextVtg + 1 ) % ch->j;
	__LCwakeUpChannel( ch );
	if( ch->altKey ) {
		__LCwakeUpAlternative( ch->altKey );
	}

	while( ! LCreadyToSend_ss(ch) ) {
		__LCwaitOnChannel( ch );
	}

	__LCunlockChannel( ch );
}

// receives a message from a channel
char *LCreceive_ss( struct channel_t *ch )
{
	char *vtg;

	__LClockChannel( ch );
	while( ! LCreadyToReceive_ss(ch) ) {
		__LCwaitOnChannel( ch );
	}

	ch->currVtg = ( ch->currVtg + 1 ) % ch->j;
	vtg = MSG(ch, ch->currVtg);
	ch->messages --;

	__LCwakeUpChannel( ch );
	__LCunlockChannel( ch );

	return vtg;
}


// prints debugging info about a channel
void printCH_ss( struct channel_t *ch )
{
	int i;

	printf( "struct channel_t: %p\n"
			"    key: %d\n"
			"     id: %d\n"
			"   size: %d\n"
			"fxdsize: %d\n"
			"      j: %d\n"
			"   next: %d\n"
			"   curr: %d\n"
			"   msgs: %d\n"
			"    alt: %d\n"
			"[%d bytes + %d per message: %d]\n",
			ch,
			ch->key,
			ch->id,
			ch->size,
			ch->fixedsize,
			ch->j,
			ch->nextVtg,
			ch->currVtg,
			ch->messages,
			ch->altKey,
			(int) sizeof(*ch), ch->size, (int) sizeof(*ch)+ch->size );

	for( i = 0; i < ch->j; i ++ ) {
		printf( " msg[%d]: %s\n", i, MSG(ch,i) );
	}
}


